#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include "pro.h"

struct cdev* cdev;
dev_t devno;
#if 0
unsigned int major = 0;
#else 
unsigned int major = 498;   //主设备号 498
#endif
unsigned int minor = 0;     //此设备号 0
struct class* cls;
struct device* dev;

//ioctl     控制LED1~3 风扇 蜂鸣器 震动马达
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
    int whitch;     //用于判断设置特定值时的目标
    int ret;        //返回值判断err
    /* 1.设置 [指定GPIO引脚] {指定值} 开 或 关
     *   因此需要{cmd}与[whitch]进行协同判断
     * 2.设置指定GPIO引脚翻转 cmd*/
    switch (cmd)
    {
    /* 1.设置 [指定GPIO引脚] {指定值} 开 或 关
     *   因此需要{cmd}与[whitch]进行协同判断 */
    case GPIO_ON:
        ret = copy_from_user(&whitch, (void *)args, sizeof(int));
        if (ret)
        {
            printk("copy_from_user failed\n");
            return -EIO;
        }
        switch (whitch)
        {
        case LED1:
            gpiod_set_value(gpiono1, 1);
            break;
        case LED2:
            gpiod_set_value(gpiono2, 1);
            break;
        case LED3:
            gpiod_set_value(gpiono3, 1);
            break;
        case FAN:
            gpiod_set_value(gpiono4, 1);
            break;
        case BUZ:
            gpiod_set_value(gpiono5, 1);
            break;
        case MOT:
            gpiod_set_value(gpiono6, 1);
            break;
        }
        break;
    case GPIO_OFF:
        ret = copy_from_user(&whitch, (void *)args, sizeof(int));
        if (ret)
        {
            printk("copy from user is error\n");
            return -EIO;
        }
        switch (whitch)
        {
        case LED1:
            gpiod_set_value(gpiono1, 0);
            break;
        case LED2:
            gpiod_set_value(gpiono2, 0);
            break;
        case LED3:
            gpiod_set_value(gpiono3, 0);
            break;
         case FAN:
            gpiod_set_value(gpiono4, 0);
            break;
        case BUZ:
            gpiod_set_value(gpiono5, 0);
            break;
        case MOT:
            gpiod_set_value(gpiono6, 0);
            break;
        }
        break;
    /* 2.设置指定GPIO引脚翻转 cmd */
    case LED1_L:
        // printk("in LED1_L\n");
        gpiod_set_value(gpiono1, !gpiod_get_value(gpiono1));
        break;
    case LED2_L:
    // printk("in LED2_L\n");
        gpiod_set_value(gpiono2, !gpiod_get_value(gpiono2));
        break;
    case LED3_L:
        gpiod_set_value(gpiono3, !gpiod_get_value(gpiono3));
        break;
    case FAN_L:
        gpiod_set_value(gpiono4, !gpiod_get_value(gpiono4));
        break;
    case BUZ_L:
        gpiod_set_value(gpiono5, !gpiod_get_value(gpiono5));
        break;
    case MOT_L:
        gpiod_set_value(gpiono6, !gpiod_get_value(gpiono6));
        break;
    }
    return 0;
}

//设置操作方法
const struct file_operations fops = {
    .unlocked_ioctl = mycdev_ioctl,
};

//platform连接函数
int pdrv_probe(struct platform_device *pdev)
{
    int ret;
    // printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    //获得LED1~3 风扇 蜂鸣器 震动马达的 GPIO编号即:gpiono[1~6]
    gpiono1 = gpiod_get_from_of_node(pdev->dev.of_node, "led1", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono1))
    {
        printk("解析gpio1编号失败\n");
        return PTR_ERR(gpiono1);
    }
    // printk("解析gpio1编号成功\n");
    gpiono2 = gpiod_get_from_of_node(pdev->dev.of_node, "led2", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono2))
    {
        printk("解析gpio2编号失败\n");
        return PTR_ERR(gpiono2);
    }
    // printk("解析gpio2编号成功\n");
    gpiono3 = gpiod_get_from_of_node(pdev->dev.of_node, "led3", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono3))
    {
        printk("解析gpio3编号失败\n");
        return PTR_ERR(gpiono3);
    }
    // printk("解析gpio3编号成功\n");
    gpiono4 = gpiod_get_from_of_node(pdev->dev.of_node, "fan", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono4))
    {
        printk("解析gpio4编号失败\n");
        return PTR_ERR(gpiono4);
    }
    // printk("解析gpio4编号成功\n");
    gpiono5 = gpiod_get_from_of_node(pdev->dev.of_node, "beep", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono5))
    {
        printk("解析gpio5编号失败\n");
        return PTR_ERR(gpiono5);
    }
    // printk("解析gpio5编号成功\n");
    gpiono6 = gpiod_get_from_of_node(pdev->dev.of_node, "motor", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono6))
    {
        printk("解析gpio6编号失败\n");
        return PTR_ERR(gpiono6);
    }
    // printk("解析gpio6编号成功\n");

    // 1.分配对象
    cdev = cdev_alloc();
    if (NULL == cdev)
    {
        printk("分配对象空间失败\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    // 2.初始化驱动对象
    cdev_init(cdev, &fops);
    if (0 == major)
    {
        ret = alloc_chrdev_region(&devno, minor, 3, "mycdev");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto ERR2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    else if (major > 0)
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "mycdev");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto ERR2;
        }
    }
    // 3.注册对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("驱动对象注册进内核失败\n");
        goto ERR3;
    }
    // printk("驱动对象注册进内核成功\n");
    cls = class_create(THIS_MODULE, "mycdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        goto ERR4;
    }
    // printk("向上提交目录成功\n");

    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "mycdev0");
    if (IS_ERR(dev))
    {
        printk("向上提交结点信息失败\n");
        goto ERR5;
    }

    return 0;
ERR5:
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), 3);
ERR2:
    kfree(cdev);
ERR1:
    return ret;

    return 0;
}

//卸载函数 释放申请的资源
int pdrv_remove(struct platform_device *pdev)
{
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    cdev_del(cdev);
    unregister_chrdev_region(MKDEV(major, minor), 3);
    kfree(cdev);
    free_irq(irqno, NULL);
    gpiod_set_value(gpiono1, 0);
    gpiod_put(gpiono1);
    gpiod_set_value(gpiono2, 0);
    gpiod_put(gpiono2);
    gpiod_set_value(gpiono3, 0);
    gpiod_put(gpiono3);
    gpiod_set_value(gpiono4, 0);
    gpiod_put(gpiono4);
    gpiod_set_value(gpiono5, 0);
    gpiod_put(gpiono5);
    gpiod_set_value(gpiono6, 0);
    gpiod_put(gpiono6);
    return 0;
}

struct of_device_id oftable[] =
    {
        {.compatible = "hqyj,platform"},
        {},
};

struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aaaa",
        .of_match_table = oftable,
    },
};

module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
